use std::rc::Rc;

fn main() {
    {
        // Define any variable.
        let x = 5;

        // Borrow `x` -- but clone it first.
        let y = &mut (x.clone());

        // Without the x.clone() two lines prior, this line would fail on
        // compile as x has been borrowed.
        // Thanks to x.clone(), x was never borrowed, and this line will run.
        println!("{x}");

        // Perform some action on the borrow to prevent Rust from optimizing
        // this out of existence.
        *y += 1;
    }

    println!();

    {
        // Define any variable.
        let mut x = Rc::new(5);

        // Borrow `x`.
        let y = Rc::make_mut(&mut x);

        println!("y = {y}");

        *y += 1;

        println!("y = {y}");
        let x_val = Rc::unwrap_or_clone(x);
        println!("x = {x_val}");
    }

    {
        // If there are other Rc pointers to the same allocation, then make_mut
        // will clone the inner value to a new allocation to ensure unique
        // ownership. This is also referred to as clone-on-write.
        //
        // https://stackoverflow.com/questions/52994205/what-is-the-standard-way-to-call-a-mutable-method-in-a-rc-wrapped-object

        let mut val = Rc::new(5);

        // Won't clone anything.
        *Rc::make_mut(&mut val) += 1;
        // Won't clone inner data.
        let mut another_val = Rc::clone(&val);
        // Clones inner data. dym: Clones val's inner data for another_val.
        *Rc::make_mut(&mut val) += 1;
        // Won't clone anything.
        *Rc::make_mut(&mut val) += 1;
        // Won't clone anything.
        *Rc::make_mut(&mut another_val) *= 2;

        // Now `val` and `another_val` point to different allocations.
        assert_eq!(8, *val);
        assert_eq!(12, *another_val);
    }
}
